/*
 * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xuexiang.xdlink.component.channel.impl;

import android.content.Context;
import android.text.TextUtils;

import com.xuexiang.xdlink.component.channel.IChannel;
import com.xuexiang.xdlink.component.channel.OnChannelListener;
import com.xuexiang.xdlink.component.channel.entity.ConnectStatusEnum;
import com.xuexiang.xdlink.exception.ChannelException;
import com.xuexiang.xdlink.logs.DCFLog;
import com.xuexiang.xdlink.util.Utils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import static com.xuexiang.xdlink.component.channel.impl.AbstractDataReceiver.DEFAULT_READ_DATA_LENGTH;

/**
 * 模拟通信通道【模拟假数据,支持数据的循环读取】
 *
 * @author xuexiang
 * @since 2018/8/26 下午4:57
 */
public class MockChannel extends AbstractChannel {

    private Context mContext;
    /**
     * MOCK文件在assets下的文件路径
     */
    private String mAssetFileName;

    /**
     * MOCK读取的文件
     */
    private File mMockFile;

    /**
     * 模拟读文件的间期
     */
    private long mReadInterval;
    /**
     * 每次读取数据的最大长度
     */
    private int mReadDataLength;
    /**
     * 是否循环读取数据
     */
    private boolean mIsLoopRead;

    /**
     * 读入模拟数据的文件流
     */
    private BufferedInputStream mBufferedInputStream;

    /**
     * 构造函数
     *
     * @param builder
     */
    MockChannel(Builder builder) {
        mContext = builder.context;
        mAssetFileName = builder.assetFileName;
        mMockFile = builder.mockFile;

        mReadInterval = builder.readInterval;
        mReadDataLength = builder.readDataLength;
        mIsLoopRead = builder.isLoopRead;
    }

    @Override
    public void openChannel() {
        if (mMockFile != null && mMockFile.exists()) {
            try {
                mBufferedInputStream = new BufferedInputStream(new FileInputStream(mMockFile));
                updateConnectStatus(ConnectStatusEnum.CONNECT_SUCCESS);

                startAcceptData(new MockChannel.MockDataReceiver(mReadDataLength, getChannelListener()));
            } catch (Exception e) {
                e.printStackTrace();
                updateConnectStatus(ConnectStatusEnum.CONNECT_FAILED);
            }
        } else if (!TextUtils.isEmpty(mAssetFileName)) {
            try {
                mBufferedInputStream = new BufferedInputStream(mContext.getAssets().open(mAssetFileName));
                updateConnectStatus(ConnectStatusEnum.CONNECT_SUCCESS);

                startAcceptData(new MockChannel.MockDataReceiver(mReadDataLength, getChannelListener()));
            } catch (IOException e) {
                e.printStackTrace();
                updateConnectStatus(ConnectStatusEnum.CONNECT_FAILED);
            }
        } else {
            updateConnectStatus(ConnectStatusEnum.CONNECT_FAILED);
        }
    }

    @Override
    public void closeChannel() {
        close();
    }

    @Override
    protected void close() {
        super.close();
        Utils.closeIO(mBufferedInputStream);
        mBufferedInputStream = null;
    }

    @Override
    public void write(byte[] message) throws ChannelException {

    }

    /**
     * 模拟数据接收
     *
     * @author xuexiang
     * @since 2018/8/26 下午5:03
     */
    protected class MockDataReceiver extends AbstractDataReceiver {

        public MockDataReceiver(int readDataLength, OnChannelListener listener) {
            super(readDataLength, listener);
            setInterval(mReadInterval);
        }

        @Override
        protected void onStart() throws IOException {
            if (mBufferedInputStream != null) {
                mInAvailable = mBufferedInputStream.available();
                if (mInAvailable > 0 && mIsLoopRead) {
                    mBufferedInputStream.mark(mInAvailable + 1); //循环前记录一下标记
                }
            }
        }

        @Override
        public void acceptData() {
            if (mBufferedInputStream != null) {
                try {
                    mInAvailable = mBufferedInputStream.available();
                    if (mInAvailable > 0) {
                        if (mInAvailable > mReadData.length) {
                            mInAvailable = mReadData.length;
                        }
                        mInAvailable = mBufferedInputStream.read(mReadData, 0, mInAvailable);
                        onDataReceived(mReadData, mInAvailable);
                    } else {
                        if (mIsLoopRead) {
                            mBufferedInputStream.reset(); //读取完毕后，重置索引指针，循环读取
                            acceptData();
                        }
                    }
                } catch (IOException e) {
                    DCFLog.e("Mock channel read data exception!", e);
                    e.printStackTrace();
                }
            }
        }

        @Override
        public IChannel getChannel() {
            return MockChannel.this;
        }
    }

    //=============================构建者==============================//
    /**
     * 构建者
     */
    public static class Builder {
        private static final int DEFAULT_READ_INTERVAL = 100;

        Context context;
        /**
         * MOCK文件在assets下的文件路径
         */
        String assetFileName;
        /**
         * MOCK读取的文件
         */
        File mockFile;
        /**
         * 模拟读文件的间期
         */
        long readInterval;
        /**
         * 每次读取数据的最大长度
         */
        int readDataLength;
        /**
         * 是否是循环读取
         */
        boolean isLoopRead;

        public Builder() {
            readInterval = DEFAULT_READ_INTERVAL;
            readDataLength = DEFAULT_READ_DATA_LENGTH;
        }

        public Builder setAssetFile(Context context, String assetFileName) {
            this.context = context.getApplicationContext();
            this.assetFileName = assetFileName;
            return this;
        }

        public Builder setMockFile(File mockFile) {
            this.mockFile = mockFile;
            return this;
        }

        public Builder setReadInterval(long readInterval) {
            this.readInterval = readInterval;
            return this;
        }

        public Builder setReadDataLength(int readDataLength) {
            this.readDataLength = readDataLength;
            return this;
        }

        public Builder setLoopRead(boolean loopRead) {
            isLoopRead = loopRead;
            return this;
        }

        public MockChannel build() {
            if ((context == null || TextUtils.isEmpty(assetFileName)) && mockFile == null) {
                throw new IllegalArgumentException("MockChannel Builder mock data source must be set！Please invoked setAssetFile or setMockFile method to set mock data source.");
            }
            return new MockChannel(this);
        }
    }


}
